//题目:
// 给你一个整数数组 coins ，表示不同面额的硬币；以及一个整数 amount ，表示总金额。
// 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额，返回 -1 。
// 你可以认为每种硬币的数量是无限的。

// 示例 1：
// 输入：coins = [1, 2, 5], amount = 11
// 输出：3 
// 解释：11 = 5 + 5 + 1

// 示例 2：
// 输入：coins = [2], amount = 3
// 输出：-1

// 示例 3：
// 输入：coins = [1], amount = 0
// 输出：0
#include<iostream>
#include<limits.h>
#include<vector>

using namespace std;
//代码
class Solution 
{
public:
    int coinChange(vector<int>& coins, int amount) 
    {
        // int n=coins.size();
        // //1.创建dp表————dp[i][j]表示：选到第i个元素、表达式和为j时，最少硬币的个数
        // vector<vector<long long>> dp(n+1,vector<long long>(amount+1,INT_MAX));
        // //2.初始化
        // dp[0][0]=0;
        // //3.填表————动态转移方程：dp[i][j]=dp[i-1][j]+dp[i-1][j-k*coins[i]];
        // for(int i=1;i<=n;i++)
        // {
        //     for(int j=0;j<=amount;j++)
        //     {
        //         // for(int k=0;k*coins[i-1]<=j;k++)
        //         //     dp[i][j]=min(dp[i][j],dp[i-1][j-k*coins[i-1]]+k);

        //         //数学优化
        //         dp[i][j]=dp[i-1][j];
        //         if(j-coins[i-1]>=0)
        //             dp[i][j]=min(dp[i-1][j],dp[i][j-coins[i-1]]+1);
        //     }
        // }
        // //4.确定返回值
        // return dp[n][amount]==INT_MAX?-1:dp[n][amount];

        //空间优化 O(N^2) ————> O(N)
        int n=coins.size();
        //1.创建dp表————dp[i][j]表示：选到第i个元素、表达式和为j时，最少硬币的个数
        vector<long long> dp(amount+1,INT_MAX);
        //2.初始化
        dp[0]=0;
        //3.填表————动态转移方程：dp[i][j]=dp[i-1][j]+dp[i-1][j-k*coins[i]];
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=amount;j++)
            {
                // for(int k=0;k*coins[i-1]<=j;k++)
                //     dp[j]=min(dp[j],dp[j-k*coins[i-1]]+k);

                // 数学优化
                if(j-coins[i-1]>=0)
                    dp[j]=min(dp[j],dp[j-coins[i-1]]+1);
            }
        }
        //4.确定返回值
        return dp[amount]==INT_MAX?-1:dp[amount];
    }
};
